package jaux.message.codec;

import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.ObjectUtils;

/**
 * The Class ByteMessage.
 *
 * @author huangdong
 */
public class ByteMessage  {

    /** The Constant serialVersionUID. */
    private static final long serialVersionUID = 554369350763133006L;

    private int[] bytes=new int[0];
    
    /** The fields. */
    private Map<String, Object> fields = new HashMap<>();
    
    /** The focus var name. */
    private String focusVarName;

    /**
     * Instantiates a new byte message.
     */
    public ByteMessage() {
    }
    public ByteMessage(String hex) {
		int[] dataBs = new int[hex.length() / 2];
		/*
		 *  将原始数据转换为字节数组，作为解析的基础
		 */
		for (int i = 0; i < hex.length(); i += 2) {
			dataBs[i / 2] =  (Integer.parseInt(hex.substring(i, i + 2), 16) & 0xff);
		}
		this.bytes = dataBs;
    }

    /**
     * To int.
     *
     * @param bs the bs
     * @return the integer
     */
    public int toInt(int[] bs) {
        return toInt(bs, bs.length * 8 - 1, 0);
    }

    /**
     * To int.
     *
     * @param bs the bs
     * @param signed the signed
     * @return the integer
     */
    public int toInt(int[] bs, boolean signed) {
        return toInt(bs, bs.length * 8 - 1, 0, signed);
    }

    /**
     * To int.
     *
     * @param b the b
     * @param start the start
     * @param end the end
     * @param signed the signed
     * @return the integer
     */
    public int toInt(int b, int start, int end, boolean signed) {
        return convertBitArrayToInt(Arrays.copyOfRange(convertByteToBitArray(b), 7 - start, 8 - end), signed);
    }

    /**
     * To int.
     *
     * @param bs the bs
     * @param start the start
     * @param end the end
     * @param signed the signed
     * @return the integer
     */
    public int toInt(int[] bs, int start, int end, boolean signed) {
        return convertBitArrayToInt(Arrays.copyOfRange(convertByteToBitArray(bs), bs.length * 8 - start - 1, bs.length * 8 - end), signed);
    }

    /**
     * To int.
     *
     * @param b the b
     * @param start the start
     * @param end the end
     * @return the integer
     */
    public int toInt(int b, int start, int end) {
        return convertBitArrayToInt(Arrays.copyOfRange(convertByteToBitArray(b), 7 - start, 8 - end));
    }

    /**
     * To int.
     *
     * @param bs the bs
     * @param start the start
     * @param end the end
     * @return the integer
     */
    public int toInt(int[] bs, int start, int end) {
        return convertBitArrayToInt(Arrays.copyOfRange(convertByteToBitArray(bs), bs.length * 8 - start - 1, bs.length * 8 - end));
    }

    /**
     * To int arr.
     *
     * @param bs the bs
     * @param step the step
     * @param start the start
     * @param end the end
     * @param signed the signed
     * @return the integer[]
     */
    public int[] toIntArr(int[] bs, int step, int start, int end, boolean signed) {
        int count = bs.length / step;
        int[] result = new int[count];
        for (int i = 0; i < count; i++) {
            result[i] = toInt(Arrays.copyOfRange(bs, i * step,(i+1)*step), start, end, signed);
        }
        return result;
    }

    /**
     * To int arr.
     *
     * @param bs the bs
     * @param step the step
     * @param start the start
     * @param end the end
     * @return the integer[]
     */
    public int[] toIntArr(int[] bs, int step, int start, int end) {
        int count = bs.length / step;
        int[] result = new int[count];
        for (int i = 0; i < count; i++) {
            result[i] = toInt(Arrays.copyOfRange(bs, i * step,(i+1)*step), start, end);
        }
        return result;
    }

    /**
     * To int arr.
     *
     * @param bs the bs
     * @return the integer[]
     */
    public int[] toIntArr(int[] bs) {
        return toIntArr(bs, 1, 7, 0);
    }

    
    public Float[] toFloatArr(int[] bs, int step ) {
    	int count = bs.length / step;
    	Float[] result = new Float[count];
    	for(int i=0;i<count;i++) {
    		result[i] = convertBitArrayToFloat(convertByteToBitArray(Arrays.copyOfRange(bs, i * step,(i+1)*step)));
    	}
    	return result;
    }
    /**
     * 转换指定的内容为浮点数
     * @param bs 字节数组
     * @param step 步长
     * @param bitStartIndex 数据起点（大端）
     * @param bitEndIndex 数据终点（大端）
     * @return
     */
    public Float[] toFloatArr(int[] bs, int step , int bitStartIndex, int bitEndIndex) {
    	int count = bs.length / step;
    	Float[] result = new Float[count];
    	for(int i=0;i<count;i++) {
    		final int[] bitArr = convertByteToBitArray(Arrays.copyOfRange(bs, i * step,(i+1)*step));
			result[i] = convertBitArrayToFloat(Arrays.copyOfRange(bitArr, bitArr.length-bitStartIndex-1, bitArr.length - bitEndIndex));
    	}
    	return result;
    }
    
    public String toStr(List<Byte> bs, String charset) throws UnsupportedEncodingException {
    	
    	byte[] ba =  new byte[bs.size()];
    	for (int i = 0; i < bs.size(); i++) {
			ba[i] = bs.get(i);

		}

		return new String(ba,charset);
    }
    
    
    public String[] toStrArr(int[] bs, int step , int bitStartIndex, int bitEndIndex) throws UnsupportedEncodingException {
    	int count = bs.length/ step;
    	String[] result = new String[count];
    	for(int i=0;i<count;i++) {
    		final int[] bitArr = convertByteToBitArray(Arrays.copyOfRange(bs, i * step,(i+1)*step));
				result[i] = convertBitArrayToString(Arrays.copyOfRange(bitArr, bitArr.length-bitStartIndex-1, bitArr.length - bitEndIndex), "UTF-8");
    	}
    	return result;
    }
    /**
     * Prints the hex.
     *
     * @param ints the ints
     * @return the string
     */
    public String printHex(int[] ints) {
        StringBuilder sb = new StringBuilder();
        for (int aint : ints) {
            sb.append(String.format("%02x", (byte)aint));
        }
        return sb.toString();
    }

    /**
     * To hex.
     *
     * @return the string
     */
    public String toHex() {
    	
    	StringBuilder sb = new StringBuilder();
    	for (int b : this.bytes) {
    		sb.append(String.format("%02X", (byte)b));
    	}
    	return sb.toString();
    }
    
    /**
     * Convert int to 4 bit array.
     *
     * @param i the i
     * @return the int[]
     */
    int[] convertIntTo4BitArray(int i) {
        return new int[]{
             ((i >> 3) & 0x1), ((i >> 2) & 0x1),
             ((i >> 1) & 0x1), ((i >> 0) & 0x1)};
    }
    
    /**
     * Convert int to bit array.
     *
     * @param i the i
     * @return the int[]
     */
    public int[] convertIntToBitArray(int i) {
    	String s = Integer.toBinaryString(i);
    	String[] ss = s.split("");
    	int[] ba = new int[ss.length];
    	for(int bi=0;bi<ss.length;bi++) {
    		ba[bi] = Integer.parseInt(ss[bi]);
    	}
    	return  ba;
    }
    
    /**
     * Convert byte to bit array.
     *
     * @param b the b
     * @return the integer[]
     */
    int[] convertByteToBitArray(int b) {
        return new int[]{((b >> 7) & 0x1), ((b >> 6) & 0x1),
             ((b >> 5) & 0x1), ((b >> 4) & 0x1),
             ((b >> 3) & 0x1), ((b >> 2) & 0x1),
             ((b >> 1) & 0x1), ((b >> 0) & 0x1)};
    }

    /**
     * Convert byte to bit array.
     *
     * @param bs the bs
     * @return the integer[]
     */
    int[] convertByteToBitArray(int[] bs) {
        int[] result = new int[bs.length*8];
        for (int i = 0; i < bs.length; i++) {
        	int[] bits = convertByteToBitArray(bs[i]);
        	for(int ii = 0;ii<8;ii++) {
        		result[i*8+ii] = bits[ii];
        	}
        }
        return result;
    }

    /**
     * Convert bit array to int.
     *
     * @param bits the bits
     * @param signed the signed
     * @return the integer
     */
    int convertBitArrayToInt(int[] bits, boolean signed) {
        if (!signed) {
            return convertBitArrayToInt(bits);
        }
        int result = 0;
        int sign = bits[0] > 0 ? -1 : 1;
        int[] bits2 = new int[32];
        for (int i = 0; i < bits2.length - bits.length; i++) {
            bits2[i] = sign > 0 ? 0 : 1;
        }
        for (int i = bits2.length - bits.length; i < bits2.length; i++) {
            bits2[i] = bits[i + bits.length - bits2.length];
        }
        for (int i = 0; i < bits2.length; i++) {
            result += (bits2[i] & 0xff) << (bits2.length - i - 1);
        }
        return result;
    }
    
    
    /**
     * Convert bit array to int.
     *
     * @param bits the bits
     * @return the integer
     */
    int convertBitArrayToInt(int[] bits) {
        int result = 0;
        for (int i = 0; i < bits.length; i++) {
            result += (bits[i] & 0xff) << (bits.length - i - 1);
        }
        return result;
    }
    /**
     * Convert bit array to float.
     *
     * @param bits the bits
     * @return the float
     */
    Float convertBitArrayToFloat(int[] bits) {
        int bitsint = convertBitArrayToInt(bits);
        
        return Float.intBitsToFloat(bitsint);
    }
    /**
     * 转换二进制为字符串.
     *
     * @param bits the bits
     * @return the string
     * @throws UnsupportedEncodingException 
     */
    String convertBitArrayToString(int[] bits, String charset) throws UnsupportedEncodingException {
    	int bytesCount = (int)Math.ceil(bits.length / 8d);
    	byte[] bytes = new byte[bytesCount];
    	int avaliableStart=-1;
    	int avaliableEnd=-1;
    	byte preByte = -1;
    	for(int i=0;i<bytesCount;i++) {
    		byte b =  (byte) convertBitArrayToInt(Arrays.copyOfRange(bits, i*8, (i+1)*8));
    		bytes[i] =  b;
    		if(avaliableStart==-1 && b!=0) {
    			avaliableStart = i;
    		}
    		if(avaliableStart >-1 && b ==0 && preByte!=0) {
    			avaliableEnd =i;
    		}
    		preByte=b;
    	}
    	return new String(Arrays.copyOfRange(bytes, avaliableStart, avaliableEnd), charset);
    }
    /**
     * Scale.
     *
     * @param origin the origin
     * @param omin the omin
     * @param omax the omax
     * @param tmin the tmin
     * @param tmax the tmax
     * @return the double
     */
    public Double scale(Number origin, Number omin, Number omax, Number tmin, Number tmax) {
    	/*
    	 * 参数完全存在时才能进行转换
    	 */
    	if(!ObjectUtils.allNotNull(origin,omin,omax,tmin,tmax))
    		return null;
    	double originValue = origin.doubleValue();
    	double ominValue = omin.doubleValue();
    	double omaxValue = omax.doubleValue();
    	/*
    	 * 原始值超出范围时不进行转换
    	 */
    	if(originValue <ominValue || originValue>omaxValue)
    		return null;
        double originRange = omax.doubleValue() - omin.doubleValue();
        double targetRange = tmax.doubleValue() - tmin.doubleValue();
        /*
         * 原始值范围为0时，目标值范围也必须为0,否则无法转换
         */
        if(originRange ==0) {
        	if(targetRange!=0) 
        		return null;
        	return tmax.doubleValue();
        	
        }
        /*
         * 正常转换
         */
        return tmin.doubleValue() + targetRange * ((origin.doubleValue() - omin.doubleValue()) / originRange);

    }

    /**
     * Sets the var.
     *
     * @param name the name
     * @param mm the mm
     */
    public void setVar(String name, Object mm) {
        this.fields.put(name, mm);
    }

    /**
     * Gets the var.
     *
     * @param name the name
     * @return the var
     */
    public Object getVar(String name) {
        return this.fields.get(name);
    }
    
    
    public Object val() {
    	return this.fields.get(this.focusVarName);
    }
    /**
     * Focus var.
     *
     * @param name the name
     */
    public void focusVar(String name) {
    	this.focusVarName = name;
    }
    
    /**
     * Blur var.
     */
    public void blurVar() {
    	this.focusVarName = null;
    }
    
    /**
     * Gets the fields.
     *
     * @return the fields
     */
    public Map<String, Object> getFields() {
        return fields;
    }

    /**
     * Sets the fields.
     *
     * @param fields the fields
     */
    public void setFields(Map<String, Object> fields) {
        this.fields = fields;
    }

    /**
     * 将属性值转换为bit数组.
     *
     * @return the int[]
     */
    public int[] toBits() {
    	int[] bits = null;
    	final Object varValue = this.fields.get(this.focusVarName);
		if(this.focusVarName !=null && varValue!=null) {
			if(varValue instanceof Integer) {
				Integer currVar = (Integer)varValue;
				bits= convertIntToBitArray(currVar.intValue());
				
			}else if(varValue instanceof Float) {
				Float currVar = (Float) varValue;
				bits = convertIntToBitArray(Float.floatToIntBits(currVar));
			}
    	}
    	return bits;
    }
    
    /**
     * 将数组型属性值转换为bit数组组成的数组
     * @return
     */
    public int[][] toBitsGroup(){
    	final Object[] varValue = (Object[])this.fields.get(this.focusVarName);
    	if(varValue==null)
    		return null;
    	int[][] bitsGroup = new int[varValue.length][];
    	for(int i=0,l=varValue.length;i<l;i++) {
    		if(varValue[i] instanceof Integer)
    			bitsGroup[i] = convertIntToBitArray((Integer)varValue[i]);
    		else if(varValue[i] instanceof Float) 
    			bitsGroup[i] = convertIntToBitArray(Float.floatToIntBits((Float)varValue[i]));
    			
    	}
    	return bitsGroup;
    }
    
    /**
     * Sets the bits.
     *
     * @param bits the bits
     * @param startByteIndex the start byte index
     * @param endByteIndex the end byte index
     * @param startBitIndex the start bit index
     * @param endBitIndex the end bit index
     * @return the int
     */
    public int setBits(int[] bits, int startByteIndex,int endByteIndex, int startBitIndex, int endBitIndex) {
    	int currSize = this.bytes.length;
    	if(endByteIndex > currSize) {
    		this.bytes = Arrays.copyOf(this.bytes, endByteIndex);
    	}
    	if(bits==null || bits.length==0)
    		return 0;
    	int start = endByteIndex * 8 -startBitIndex;
    	int end = endByteIndex * 8 -endBitIndex;
    	int[] ba = this.toBitArray();
    	for(int i=0;i<bits.length;i++) {
    		ba[end-bits.length+i] = bits[i];
    	}
    	this.setBitArray(ba);
    	return bits.length;
    }
    /**
     * Sets the hex.
     *
     * @param hex the hex
     * @param startByteIndex the start byte index
     * @param endByteIndex the end byte index
     * @return the int
     */
    public int setHex(int startByteIndex,int endByteIndex ) {
    	final Object hex = this.fields.get(this.focusVarName);
    	if(!(hex instanceof String))
    		return -1;
    	int mod = hex.toString().length() % 2;
    	String hexStr = (String)hex;
    	if(mod>0)
    		hexStr = '0'+hexStr;
    	for(int i=0;i<hexStr.length();i+=2) {
    		String item = hexStr.substring(i, i+2);
    		Integer intValue = Integer.valueOf(item, 16);
    		int[] bits = convertIntToBitArray(intValue);
    		setBits(bits, startByteIndex+i/2, startByteIndex+i/2+1, 7, 0);
    	}
    	return 0;
    }
    /**
     * 将bit数组集合设置到消息的指定的位置.
     *
     * @param bitsGroup the bits group
     * @param startByteIndex the start byte index
     * @param endByteIndex the end byte index
     * @param step the step
     * @param startBitIndex the start bit index
     * @param endBitIndex the end bit index
     * @return 设置的bit数量
     */
    public int setBitsGroup(int[][] bitsGroup, int startByteIndex,int endByteIndex, int step,int startBitIndex, int endBitIndex) {
    	int currSize = this.bytes.length;
    	if(endByteIndex > currSize) {
    		this.bytes = Arrays.copyOf(this.bytes, endByteIndex);
    	}
    	if(bitsGroup==null || bitsGroup.length==0)
    		return 0;
    	int settingCount=0;
    	int[] ba = this.toBitArray();
    	int byteRange = endByteIndex - startByteIndex;
    	int count = Math.min(bitsGroup.length, byteRange / step);
    	for(int i=0;i<count;i++) {
    		int currEndByteIndex = startByteIndex + step* (i+1);
    		int start = currEndByteIndex * 8 -startBitIndex;
    		int end = currEndByteIndex * 8 -endBitIndex;
    		int[] bits = bitsGroup[i];
    		for(int n=0;n<bits.length;n++) {
    			
    			ba[end-bits.length+n] = bits[n];
    		}
    		settingCount+=bits.length;
    	}
    	this.setBitArray(ba);
    	return settingCount;
    }
    /**
     * 返回消息内容由0,1组成的bit数组的表示结果.
     *
     * @return bit数组
     */
    public int[] toBitArray() {
    	int[] bitArr = new int[this.bytes.length*8];
    	for(int i=0;i<this.size();i++) {
    		int[] ba = convertByteToBitArray(this.get(i));
    		for(int j=0;j<8;j++) {
    			bitArr[i*8+j] = ba[j];
    		}
    	}
    	return bitArr;
    }
    
    /**
     * 使用bit数组填充消息体。
     *
     * @param bitArray 由0,1的整型值组成的bit数组
     */
    public void setBitArray(int[] bitArray) {
    	this.clear();
    	int size =(int)Math.ceil( bitArray.length / 8d);
    	int[] leftPadBitArray = new int[size*8];
    	Arrays.fill(leftPadBitArray, 0);
    	int offset = size*8-bitArray.length;
    	for(int i=0;i<bitArray.length;i++) {
    		leftPadBitArray[i+offset] = bitArray[i]; 
    	}
    	
    	for(int i=0;i<size;i++) {
    		this.add((byte) convertBitArrayToInt(Arrays.copyOfRange(leftPadBitArray, i*8, (i+1)*8)));
    	}
    }
    
    
    public Double keepDecimals(Double origin, int decimals) {
    	if(origin == null)
    		return null;
    	BigDecimal   b   =   new   BigDecimal(origin);  
    	double   f1   =   b.setScale(decimals,   BigDecimal.ROUND_HALF_UP).doubleValue(); 
    	return f1;
    }
    
    public int sumCheck(int start, int end) {
    	int i=0;
    	for(int b: Arrays.copyOfRange(bytes,start,end)) {
    		i+=b;
    	}
    	return i;
    }
    /**
     * The main method.
     *
     * @param args the arguments
     */
    public static void main(String[] args) {
		ByteMessage bm = new ByteMessage();
		bm.setVar("age", 87);
		bm.focusVar("age");
		System.out.println(Arrays.toString(bm.toBits()));
		bm.setBits(bm.toBits(), 1, 3, 15, 0);
		System.out.println(bm);
		
		System.out.println(Arrays.toString(bm.toBitArray()));
		bm.setBitArray(new int[] {1,1,1,1,0,1,1,1,1});
		System.out.println(Arrays.toString(bm.toBitArray()));
	}

	public int size() {
		return this.bytes.length;
	}
    
	public int get(int i) {
		return this.bytes[i];
	}
	
	public void clear() {
		this.bytes = new int[0];
	}
	
	public void add(byte b) {
		this.bytes = Arrays.copyOf(bytes, this.bytes.length+1);
		this.bytes[this.bytes.length-1] = b;
	}
	
	public int[] range(int start, int end) {
		return Arrays.copyOfRange(bytes, start, end);
	}
	
	public int[] getBytes() {
		return bytes;
	}

	
	public void setBytes(int[] bytes) {
		this.bytes = bytes;
	}

	
	public String getFocusVarName() {
		return focusVarName;
	}

	
	public void setFocusVarName(String focusVarName) {
		this.focusVarName = focusVarName;
	}
    
}
